<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="../assets/css/base.css">
</head>
<body>
    <div id="app">
        <pre v-pre class="note">
        侦听器 (监视器) (watch)
            功能：构建一个对Vue实例中数据仓库中变量（data，computed）的监控方法，实现当数据变化时执行额外扩展方法
            实现：在vue劫持数据时，对Object.defineProperty的set进行了代码的设置，让开发这可以将自定义的逻辑写入变量变化的触发方法中；
            语法：
                + 配置方式
                    watch:{
                        // key 当前vue实例需要被 侦听 的变量名称
                        // key 针对于*对象属性*的监控，可以使用 属性取值的 路径字符串格式 作为key ==> 例如 user:{ name:"tom" } ==> "user.name":value
                        // value:Function 为当前该变量被修改时 需要执行的方法 【存在两个形成 newValue,oldValue】
                        // value:Object={ 
                        //    handler: Function 为当前该变量被修改时 需要执行的方法 【存在两个形成 newValue,oldValue】
                        //    deep: Boolean(默认值false) 是否开启深度监视 【 如果数据为引用类型数据，开启该功能表示不仅监控栈，也监控堆 】
                        //                        注意:引用数据开启深度操作功能，此时只修改堆数据触发的回调的 newValue 和 oldValue 为相同数据
                        //    immediate: Boolean 是否开启初始化触发 - 初始化直接调用时 回调函数的 olvValue 没有值
                        // }
                        // value:Array 表示一次性为一个监控变量提供多个回调执行方法，回调为无序执行
                        // key:value
                    }
            特性: 因为侦听器实际上时对 劫持功能 set 方法的扩展，所基于defineProperty实现的监视器*默认*只能监控到变量 “栈” 的变化
        </pre>
        <p>msg:{{ msg }}</p>
        <input type="button" value="changeMsg" @click="changeMsg()">
        <br>
        <p>info:{{ info }}</p>
        msg<input v-model="msg" type="text" /><br>
        ext<input v-model="ext" type="text" />
        
        <hr>
        <p>arr:{{ arr }}</p>
        <ul>
            <li v-for="(item,i) in arr">
                <input v-model="arr[i]" type="number" />
            </li>
        </ul>
        <input type="button" value="resetArr" @click="arr=[111,222,333,444]">
        <br>
        <p>user:{{ user }}</p>
        <ul>
            <li>
                name:<input v-model="user.name" type="text" />
            </li>
            <li>
                age:<input v-model="user.age" type="number" />
            </li>
        </ul>
        <input type="button" value="resetUser" @click="user={ name:'jack',age:33 }">
        <hr>
        <p>num:{{ num }}</p>
        <input type="button" value="-" @click="num--">
        <input type="button" value="+" @click="num++">
    </div>
    <script src="../assets/mockjs/mock-min.js"></script>
    <script type="module">
        import { createApp } from "../assets/vue3/vue.esm-browser.js";
        createApp({
            data() {
                return {
                    msg:"msg变量",
                    ext:".end",
                    arr:[1,2,3,4],
                    user:{
                        name:"tom",
                        age:23
                    },
                    num:100
                }
            },
            computed: {
                // 定义只读计算属性
                // Object.defineProperty(vue实例,"info",{
                //     get(){
                //         return ……
                //     }
                // }
                info() {
                    return this.msg + this.ext 
                }
            },
            watch: {
                // Object.defineProperty(vue实例,"msg",{
                //     get(){
                //         return ……
                //     },
                //     set(nv){
                //         watch定义该变量监视方法的回调
                //         赋值操作;
                //         DOM的更新操作;
                //     }
                // })
                msg:function(nv,ov){
                    console.log("msg被修改-侦听器-watch:",nv,ov)
                },
                // 计算属性的监视器定义，vue会深入到计算属性代码内部的 其它仓库变量上【所有有关的变量】，完成监视器注册
                info(nv,ov){
                    console.log("info被修改-侦听器-watch:",nv,ov)
                },
                // Object.defineProperty(vue实例,"arr",{
                //     get(){
                //         return ……
                //     },
                //     set(nv){
                //         watch定义该变量监视方法的回调
                //         赋值操作;
                //         DOM的更新操作;
                //     }
                // })
                // arr(nv,ov){
                //     console.log("arr被修改-侦听器-watch:",nv,ov)
                // }
                arr:{
                    handler(nv,ov){
                        console.log("arr被修改-侦听器-watch:",nv,ov)
                    },
                    deep:true
                },
                // 因为vue程序在完成数据劫持时，不会对数组进行元素的劫持操作，所以没有数组元素的set方法，因此无法监控
                // "arr[0]":function(nv,ov){
                //     console.log("arr[0]被修改-侦听器-watch:",nv,ov)
                // },
                // user(nv,ov){
                //     console.log("user被修改-侦听器-watch:",nv,ov)
                // }
                user:{
                    handler(nv,ov){
                        console.log(nv==ov)
                        console.log("user被修改-侦听器-watch:",nv,ov)
                    },
                    deep:true,
                    immediate:true
                },
                "user.name":function(nv,ov){
                    console.log("user.name被修改-侦听器-watch:",nv,ov)
                },
                num:[
                    function(nv,ov){
                        console.log("num被修改-侦听器-1",nv,ov)
                    },
                    function(nv,ov){
                        console.log("num被修改-侦听器-2",nv,ov)
                    },
                    function(nv,ov){
                        console.log("num被修改-侦听器-3",nv,ov)
                    },
                    function(nv,ov){
                        console.log("num被修改-侦听器-4",nv,ov)
                    }
                ]
            },
            methods: {
                changeMsg() {
                    this.msg = Mock.mock("@cword(5,5)")
                    console.log("msg被修改-事件-methods")
                }
            },
        }).mount("#app")
    </script>
</body>
</html>